package com.right.health.app.widget;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.NinePatch;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.NinePatchDrawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

import com.magic.basic.utils.CollectionUtil;
import com.magic.basic.utils.ScreenUtil;
import com.right.health.app.R;
import com.right.health.app.app.App;

import java.util.ArrayList;
import java.util.List;

/*
 * Created by hopeliao on 2019-10-05
 *
 */
public class HealthSeekBar extends View {

    private float mNormalMinValue;
    private float mNormalMaxValue;

    private float mStart;
    private float mEnd;

    private int mUnit;

    private Bitmap mMaxBitmap, mMinBitmap, mMiddleBitmap;

    private List<PositionRef> mPositions = new ArrayList<>();

    private int mWidth, mHeight;

    private boolean mNeedUpdate = true;

    private int mMaxLength;

    private int mLineHeight;

    private Paint mLinePaint, mDefaultPaint;

    private float mProgress = 0.0f;

    //两边预留的边距
    private int mPadding;

    private List<TextPositionRef> mTexts;

    private Paint mTipsPaint, mProgressPaint, mRedLinePaint;

    private ProgressRef mProgressRef;

    private Rect mLineRect = new Rect();
    private Rect mNormalMinRect = new Rect();
    private Rect mNormalMaxRect = new Rect();

    private OnSeekBarChangeListener mListener;


    public HealthSeekBar(Context context) {
        super(context);
        init(null);
    }

    public HealthSeekBar(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(attrs);
    }

    public HealthSeekBar(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(attrs);
    }

    private void init(AttributeSet attrs) {

        final TypedArray array = getContext().obtainStyledAttributes(attrs, R.styleable.HealthSeekBar);
        if (array != null) {
            mStart = array.getFloat(R.styleable.HealthSeekBar_start, 0.0f);
            mEnd = array.getFloat(R.styleable.HealthSeekBar_end, 0.0f);

            mNormalMinValue = array.getFloat(R.styleable.HealthSeekBar_normalMinValue, 0.0f);
            mNormalMaxValue = array.getFloat(R.styleable.HealthSeekBar_normalMaxValue, 0.0f);

            mUnit = array.getInt(R.styleable.HealthSeekBar_unit, 10);
        }

        mLineHeight = getResources().getDimensionPixelOffset(R.dimen.dp_4);

        int defaultWidth = getResources().getDimensionPixelOffset(R.dimen.dp_1);

        int defaultColor = getResources().getColor(R.color.color_CECECE);

        mProgressRef = new ProgressRef();
        Bitmap resourceBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_health_seek);
        if (NinePatch.isNinePatchChunk(resourceBitmap.getNinePatchChunk())) {
            NinePatchDrawable ninePatchDrawable = new NinePatchDrawable(getResources(), resourceBitmap, resourceBitmap.getNinePatchChunk(), new Rect(), null);
            Bitmap bitmap = Bitmap.createBitmap(
                    ninePatchDrawable.getIntrinsicWidth(),
                    ninePatchDrawable.getIntrinsicHeight(),
                    ninePatchDrawable.getOpacity() != PixelFormat.OPAQUE ?
                            Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565);
            Canvas canvas = new Canvas(bitmap);
            ninePatchDrawable.setBounds(0, 0, ninePatchDrawable.getIntrinsicWidth(),
                    ninePatchDrawable.getIntrinsicHeight());
            ninePatchDrawable.draw(canvas);

//            mProgressRef.setBitmap(Bitmap.createBitmap(bitmap, 0, 0,
//                    getResources().getDimensionPixelOffset(R.dimen.dp_28), getResources().getDimensionPixelOffset(R.dimen.dp_20)));
//            bitmap.recycle();
            resourceBitmap.recycle();
            mProgressRef.setBitmap(bitmap);
        } else {
            mProgressRef.setBitmap(resourceBitmap);
//            mProgressRef.setBitmap(Bitmap.createBitmap(resourceBitmap, 0, 0,
//                    getResources().getDimensionPixelOffset(R.dimen.dp_28), getResources().getDimensionPixelOffset(R.dimen.dp_20)));
        }

//        resourceBitmap.recycle();

        mMinBitmap = Bitmap.createBitmap(defaultWidth, getResources().getDimensionPixelOffset(R.dimen.dp_3), Bitmap.Config.ARGB_8888);
        mMinBitmap.eraseColor(defaultColor);

        mMiddleBitmap = Bitmap.createBitmap(defaultWidth, getResources().getDimensionPixelOffset(R.dimen.dp_5), Bitmap.Config.ARGB_8888);
        mMiddleBitmap.eraseColor(defaultColor);

        mMaxBitmap = Bitmap.createBitmap(defaultWidth, getResources().getDimensionPixelOffset(R.dimen.dp_10), Bitmap.Config.ARGB_8888);
        mMaxBitmap.eraseColor(defaultColor);

        mPadding = getResources().getDimensionPixelOffset(R.dimen.dp_18);

        mMaxLength = (int) (mEnd - mStart) * mUnit + 1;

        mLinePaint = new Paint();
        mLinePaint.setAntiAlias(true);
        mLinePaint.setColor(App.getInstance().getResources().getColor(R.color.color_1BBB61));

        mRedLinePaint = new Paint();
        mRedLinePaint.setAntiAlias(true);
        mRedLinePaint.setColor(getResources().getColor(R.color.color_EA3C3C));

        mDefaultPaint = new Paint();
        mDefaultPaint.setAntiAlias(true);

        mTipsPaint = new Paint();
        mTipsPaint.setAntiAlias(true);
        mTipsPaint.setColor(getResources().getColor(R.color.color_999999));
        mTipsPaint.setTextSize(getResources().getDimensionPixelSize(R.dimen.sp_10));

        mProgressPaint = new Paint();
        mProgressPaint.setAntiAlias(true);
        mProgressPaint.setColor(getResources().getColor(R.color.white));
        mProgressPaint.setTextSize(getResources().getDimensionPixelSize(R.dimen.sp_12));
    }

    public void setTextIndex(int... index) {
        mTexts = new ArrayList<>(index.length);

        for (int i = 0; i < index.length; i++) {
            TextPositionRef textPositionRef = new TextPositionRef();

            textPositionRef.index = index[i];
            textPositionRef.value = String.valueOf(index[i]);
            mTexts.add(i, textPositionRef);
        }
        calculateText();
    }

    private void calculateProgress() {
        if (!CollectionUtil.isEmpty(mPositions)) {
            float value = mStart + mProgress * (mEnd - mStart);
            float text = (float) (Math.round(value * 10)) / 10;
            mProgressRef.text = String.valueOf(text);

            float y = Float.MAX_VALUE;

            for (int i = 0; i < mPositions.size(); i++) {
                PositionRef ref = mPositions.get(i);

                y = Math.min(y, ref.mDst.top);
            }
            int left = mPadding;
            int right = mWidth - mPadding;

            int centerX = (int) (left + (right - left) * mProgress + 0.5f);

            Rect rect = new Rect();
            mProgressPaint.getTextBounds(mProgressRef.text, 0, mProgressRef.text.length(), rect);
            int w = rect.width();
            int h = rect.height();

            mProgressRef.textPoint.x = centerX - w / 2;
            mProgressRef.textPoint.y = (int) y - getResources().getDimensionPixelOffset(R.dimen.dp_10);

            int offset = getResources().getDimensionPixelOffset(R.dimen.dp_5);

            mProgressRef.mDst.set(mProgressRef.textPoint.x - offset, mProgressRef.textPoint.y - h - offset, mProgressRef.textPoint.x + offset + w, (int) y);
        }
    }

    private void calculateText() {
        if (!CollectionUtil.isEmpty(mTexts) && !CollectionUtil.isEmpty(mPositions)) {
            for (int i = 0; i < mPositions.size(); i++) {

                for (TextPositionRef ref : mTexts) {

                    if (ref.index * mUnit == mPositions.get(i).index) {
                        RectF dst = mPositions.get(i).mDst;

                        Rect rect = new Rect();
                        mTipsPaint.getTextBounds(ref.value, 0, ref.value.length(), rect);
                        int w = rect.width();
                        int h = rect.height();

                        ref.pointF.x = (dst.left + dst.right) / 2.0f - (w / 2.0f);
                        ref.pointF.y = dst.top - getResources().getDimensionPixelOffset(R.dimen.dp_5) - h;
                    }
                }
            }
            invalidate();
        }
    }


    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        mNeedUpdate = mWidth != getWidth();

        mWidth = getWidth();
        mHeight = getHeight();
        calculatePosition();
        calculateText();
        calculateProgress();
    }

    public float getMaxValue() {
        return mEnd;
    }

    public float getMinValue() {
        return mStart;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int width = getMeasureSize(ScreenUtil.getInstance(getContext()).getScreenWidth(), widthMeasureSpec);
        int height = getMeasureSize(getResources().getDimensionPixelOffset(R.dimen.dp_50), heightMeasureSpec);
        setMeasuredDimension(width, height);
    }

    private int getMeasureSize(int size, int measureSpec) {
        int result = size;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);

        switch (specMode) {
            // Mode = UNSPECIFIED,AT_MOST时使用提供的默认大小
            case MeasureSpec.UNSPECIFIED:
                result = size;
                break;
            // Mode = EXACTLY时使用测量的大小
            case MeasureSpec.AT_MOST:
            case MeasureSpec.EXACTLY:
                result = specSize;
                break;
        }
        return result;
    }


    private void calculatePosition() {
        if (mNeedUpdate) {
            if (mPositions.size() == 0) {
                for (int i = 0; i < mMaxLength; i++) {
                    mPositions.add(new PositionRef());
                }
            }
            mLineRect.set(mPadding, mHeight - mLineHeight, mWidth - mPadding, mHeight);
            if (mNormalMinValue > 0) {
                float value = (mNormalMinValue - mStart) / (mEnd - mStart);
                mNormalMinRect.set(mPadding, mHeight - mLineHeight,  mPadding + (int) (((mWidth - mPadding * 2) * value + 0.5f)), mHeight);
            }
            if (mNormalMaxValue > 0) {
                float value = (mNormalMaxValue - mStart) / (mEnd - mStart);
                mNormalMaxRect.set(mPadding + (int) (((mWidth - mPadding * 2) * value + 0.5f)), mHeight - mLineHeight, mWidth - mPadding, mHeight);
            }

            float offset = (mWidth - (mPadding * 2) * 1.0f) / (mMaxLength * 1.0f);

            int defaultY = mLineRect.top - getResources().getDimensionPixelOffset(R.dimen.dp_6);

            for (int i = 0; i < mMaxLength; i++) {
                PositionRef ref = mPositions.get(i);
                if (i % 10 == 0) {
                    ref.bitmap = mMaxBitmap;
                } else if (i % 5 == 0) {
                    ref.bitmap = mMiddleBitmap;
                } else {
                    ref.bitmap = mMinBitmap;
                }

                ref.index = (int)mStart + i;

                ref.mSrc.set(0, 0, ref.bitmap.getWidth(), ref.bitmap.getHeight());
                ref.mDst.set(offset * i + mPadding, defaultY - ref.bitmap.getHeight(), offset * i + ref.bitmap.getWidth() + mPadding, defaultY);

            }
        }
    }

    private float mDownX, mDownY;

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int action = event.getAction();
        float x = event.getX();
        float y = event.getY();
        if (mLineRect != null && y >= mLineRect.left && y < mLineRect.right) {
            switch (action) {
                case MotionEvent.ACTION_MOVE:
                    if (Math.abs(x - mDownX) > Math.abs(y - mDownY)) {
                        getParent().requestDisallowInterceptTouchEvent(true);
                    } else {
                        getParent().requestDisallowInterceptTouchEvent(false);
                    }
                    moveToProgress(x - mPadding);
                    break;
                case MotionEvent.ACTION_DOWN:
                    mDownX = x;
                    mDownY = y;
                    moveToProgress(x - mPadding);

                    getParent().requestDisallowInterceptTouchEvent(true);
                    return true;
            }
        }

        return super.onTouchEvent(event);
    }

    private void moveToProgress(float x) {
        if (mWidth != 0) {
            mProgress = Math.max(0.0f, Math.min(x / mLineRect.width(), 1.0f));

            if(mListener != null) {
                mListener.onProgressChanged(this, mProgress, false);
            }

            calculateProgress();
            invalidate();
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawRect(mLineRect, mLinePaint);

        if (!CollectionUtil.isEmpty(mPositions)) {
            for (int i = 0; i < mPositions.size(); i++) {
                PositionRef ref = mPositions.get(i);
                canvas.drawBitmap(ref.bitmap, ref.mSrc, ref.mDst, mDefaultPaint);
            }
        }

        if (!CollectionUtil.isEmpty(mTexts)) {
            for (TextPositionRef ref : mTexts) {
                canvas.drawText(ref.value, ref.pointF.x, ref.pointF.y, mTipsPaint);
            }
        }

        if (mProgressRef != null) {
            canvas.drawBitmap(mProgressRef.bitmap, mProgressRef.mSrc, mProgressRef.mDst, mDefaultPaint);
            canvas.drawText(mProgressRef.text, mProgressRef.textPoint.x, mProgressRef.textPoint.y, mProgressPaint);
        }

        if(mNormalMinValue > 0) {
            canvas.drawRect(mNormalMinRect, mRedLinePaint);
        }
        if(mNormalMaxValue > 0) {
            canvas.drawRect(mNormalMaxRect, mRedLinePaint);
        }

    }

    public float getProgress() {
        return mProgress;
    }

    public void setProgress(int progress) {
        this.mProgress = progress;
    }

    private class PositionRef {
        PositionRef() {
            mSrc = new Rect();
            mDst = new RectF();
        }

        private Rect mSrc;
        private RectF mDst;

        private Bitmap bitmap;
        private int index;
    }

    private class TextPositionRef {

        private int index;
        private String value;
        private PointF pointF;

        TextPositionRef() {
            pointF = new PointF();
        }
    }

    private class ProgressRef {
        ProgressRef() {
            mSrc = new Rect();
            mDst = new Rect();
            textPoint = new Point();
        }

        private String text;
        private Bitmap bitmap;
        private Rect mSrc, mDst;
        private Point textPoint;

        void setBitmap(Bitmap bitmap) {
            this.bitmap = bitmap;
            mSrc.set(0, 0, this.bitmap.getWidth(), this.bitmap.getHeight());
        }
    }

    public interface OnSeekBarChangeListener {
        void onProgressChanged(HealthSeekBar seekBar, float progress, boolean fromUser);
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        if (mProgressRef != null && mProgressRef.bitmap != null) {
            mProgressRef.bitmap.recycle();
            mProgressRef.bitmap = null;
        }
        if (mMinBitmap != null && !mMinBitmap.isRecycled()) {
            mMinBitmap.recycle();
            mMinBitmap = null;
        }
        if (mMaxBitmap != null && !mMaxBitmap.isRecycled()) {
            mMaxBitmap.recycle();
            mMaxBitmap = null;
        }
        if (mMiddleBitmap != null && !mMiddleBitmap.isRecycled()) {
            mMiddleBitmap.recycle();
            mMiddleBitmap = null;
        }
    }

    public void setOnSeekBarChangeListener(OnSeekBarChangeListener listener) {
        this.mListener = listener;
    }
}
